<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Docker 入门实践教程</title>
    <link rel="stylesheet" href="tutorial_style.css">
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700&display=swap" rel="stylesheet">
</head>
<body>

    <div class="container">
        <aside class="sidebar">
            <div class="sidebar-header">
                <h3>📘 教程目录</h3>
            </div>
            <nav id="table-of-contents">
                <ul>
                    <li><a href="#section-0" class="active">1. 应用功能揭秘</a></li>
                    <li><a href="#section-1">2. Docker是什么？</a></li>
                    <li><a href="#section-2">3. 准备工作</a></li>
                    <li><a href="#section-3">4. 核心概念：Dockerfile</a></li>
                    <li><a href="#section-4">5. 关键操作：构建镜像</a></li>
                    <li><a href="#section-5">6. 运行与访问</a></li>
                    <li><a href="#section-6">7. 容器管理</a></li>
                    <li><a href="#section-7">8. 分享你的成果</a></li>
                    <li><a href="#section-8">9. 总结与下一步</a></li>
                </ul>
            </nav>
        </aside>

        <main class="content">
            <header>
                <h1>Docker 入门实践教程：从零开始容器化你的第一个Web应用</h1>
                <p>你好！欢迎来到Docker的世界。本教程将通过一个真实的案例——"配置字解析工具"，带你一步步学习Docker的核心概念和基本操作。即使你之前从未接触过Docker，也能轻松上手。</p>
            </header>

            <section id="section-0">
                <h2>1. 应用功能揭秘：配置字是如何被解析和修改的？</h2>
                <p>在我们深入Docker的世界之前，先来了解一下我们正在处理的这个"配置字解析工具"究竟是做什么的。这会帮助你更好地理解为什么我们需要它。</p>
                
                <div class="highlight-box">
                    <h4>核心任务</h4>
                    <p>将一长串像天书一样的车辆代码（配置字），翻译成我们能看懂的功能列表，并且能让我们安全地修改这些功能，再把它们变回代码。</p>
                </div>
            
                <h3>第 1 步：解码 - 从"天书"到"列表"</h3>
                <p>想象一下，你拿到了一段很长的密码电报，比如这样：</p>
                <div class="code-block"><pre><code>5A001000... (一长串由数字和字母组成的字符)</code></pre></div>
                <p>单独看它，我们完全不知道是什么意思。但我们有一本"密码本"，在我们的项目中，它就是 <code>codeDID.xlsx</code> 文件。</p>
                <p>这本"密码本"告诉我们：</p>
                <ul>
                    <li>电报的第 1 到第 8位，代表"车型"。</li>
                    <li>第 17 位，代表"有没有自适应巡航(ADCC)"。</li>
                    <li>第 128 到 135 位，代表"销售区域"。</li>
                </ul>

                <div class="tech-note-box">
                    <h4><span class="emoji">⚠️</span> 特别注意：一个重要的技术细节</h4>
                    <p>在解析时有一个关键规则：<strong>字节内反序</strong>。这意味着在一个字节（8个bit位）的内部，我们读取bit的顺序是颠倒的（从右到左，而不是从左到右）。</p>
                    
                    <p><strong>举个例子：</strong></p>
                    <p>我们来看配置项 <code>FLBMS</code>，密码本 (`codeDID.xlsx`) 上说它的信息如下：</p>
                    <ul>
                        <li>位置: 第 14 bit</li>
                        <li>字节: 2 (因为第1个字节是1-8 bit, 第2个字节是9-16 bit)</li>
                        <li>含义: 0 = 不存在, 1 = 存在</li>
                    </ul>
                    <p>假设我们配置字的第2个字节是十六进制的 <code>3C</code>，它换算成二进制就是 <code>00111100</code>。</p>
                    <p>因为字节内是反序的，我们需要从右往左数来找到属于第14个bit的值。第2个字节包含了9-16这8个bit，它们的内部顺序是：</p>
                    <pre><code>二进制:  0  0  1  1  1  1  0  0
绝对位置: 16 15 14 13 12 11 10  9</code></pre>
                    <p>我们可以清楚地看到，处在第14 bit位置上的值是 <code>1</code>。所以，工具就会解析出FLBMS的状态是"存在"。</p>

                    <p>这就像我们习惯从左到右写字，但有些古文需要从右到左读一样。我们的工具已经自动处理了这个复杂的细节，你无需担心。但了解这一点，有助于你更深入地理解配置字真正的结构。</p>
                </div>

                <p>我们的工具所做的第一件事，就是根据这本"密码本"，把长长的代码自动翻译成一个清晰的表格，告诉你每个功能的当前状态。</p>
            
                <h3>第 2 步：修改 - 可视化的"点选"操作</h3>
                <p>现在，你看到了翻译后的列表。比如，你发现"销售区域"当前是"欧洲"，但你想把它改成"亚洲"。</p>
                <p>你不需要去研究如何修改那段长长的代码。你只需要在网页上找到"销售区域"这一行，然后从下拉菜单中选择"亚洲"。</p>
            
                <h3>第 3 步：重新编码 - 从"列表"回到"天书"</h3>
                <p>这正是最神奇的地方！</p>
                <p>当你选择了"亚洲"后，工具会立刻反向查询"密码本"：</p>
                <ol>
                    <li>它知道"销售区域"对应的是第 128 到 135 位。</li>
                    <li>它也知道"亚洲"这个选项应该转换成新的二进制代码（比如 <code>0101</code>）。</li>
                    <li>然后，它会精确地找到原始长代码中的那几个位置，用代表"亚洲"的新代码，替换掉原来代表"欧洲"的旧代码。</li>
                </ol>
                <p>这个过程是瞬间完成的。你会看到，页面顶部输入框里的那串长代码，在你修改选项的瞬间，也跟着改变了！</p>
                
                <h3>总结</h3>
                <p>所以，这个工具就像一个双向翻译器。它帮助我们：</p>
                <ul>
                    <li><span class="emoji">🔍</span> <strong>读懂</strong> 复杂的配置代码。</li>
                    <li><span class="emoji">✏️</span> <strong>安全地修改</strong>，避免了手动修改代码时可能出现的错误。</li>
                </ul>
                <p>了解了它的工作原理后，我们接下来就看看如何用Docker这个强大的"集装箱"技术，把这个好用的工具打包和分享给任何人。</p>
            </section>

            <section id="section-1">
                <h2>2. Docker是什么？为什么需要它？</h2>
                <p>想象一下，你精心开发了一个网站，在你的电脑上运行得非常完美。但当你把它交给朋友时，却因为环境不同（比如缺了某个软件、版本不对）而无法运行。</p>
                <p><strong>Docker就像一个标准化的"集装箱"</strong>，它把你开发的应用（代码）和它需要的所有环境（软件、库、配置）全部打包在一起。无论这个"集装箱"被搬到哪里（朋友的电脑、公司的服务器），里面的应用都能以同样的方式完美运行。</p>
                <div class="highlight-box">
                    <h4>使用Docker的好处：</h4>
                    <ul>
                        <li><span class="emoji">🌍</span> <strong>环境一致性</strong>：告别"在我电脑上明明是好的"这种尴尬。</li>
                        <li><span class="emoji">🚀</span> <strong>快速部署</strong>：一键启动，无需繁琐的环境配置。</li>
                        <li><span class="emoji">🛡️</span> <strong>隔离性</strong>：每个容器都是独立的，互不干扰。</li>
                    </ul>
                </div>
            </section>

            <section id="section-2">
                <h2>3. 准备工作：安装Docker</h2>
                <p>首先，你需要安装Docker Desktop。</p>
                <p><a href="https://www.docker.com/products/docker-desktop/" target="_blank" class="button">访问Docker官网下载</a></p>
                <p>安装完成后，打开你的命令行工具（如PowerShell、CMD或终端），输入以下命令验证是否成功：</p>
                <div class="code-block">
                    <pre><code>docker --version</code></pre>
                    <button class="copy-btn">复制</button>
                </div>
                <p>如果能看到版本号，说明安装成功！</p>
            </section>

            <section id="section-3">
                <h2>4. 核心概念：Dockerfile</h2>
                <p><code>Dockerfile</code> 是一个文本文件，它记录了如何一步步构建一个Docker镜像的"说明书"或"菜谱"。下面是一个更完善的版本，它增加了<code>ENV</code>来设置环境变量，以及一个<code>HEALTHCHECK</code>指令。<code>HEALTHCHECK</code>非常有用，它可以告诉Docker如何检查我们的应用是否还在健康运行。</p>
                <div class="code-block">
<pre><code># 配置字解析工具 Dockerfile (本地镜像版)
# 使用本地已有的Node.js镜像
FROM node:18-alpine

# 设置工作目录
WORKDIR /app

# 设置环境变量
ENV NODE_ENV=production
ENV PORT=3001

# 安装必要的系统包
RUN apk add --no-cache wget curl

# 复制应用文件
COPY . .

# 暴露端口
EXPOSE 3001

# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \
  CMD wget --no-verbose --tries=1 --spider http://localhost:3001/ || exit 1

# 启动命令
CMD ["npx", "http-server", ".", "-p", "3001", "-a", "0.0.0.0", "--cors", "-c-1"]</code></pre>
                    <button class="copy-btn">复制</button>
                </div>
            </section>

            <section id="section-4">
                <h2>5. 关键操作：构建你的第一个镜像（解决网络问题）</h2>
                <p>镜像是应用的静态"快照"，容器是镜像的动态"实例"。我们需要先根据 <code>Dockerfile</code> 构建出镜像。</p>
                
                <h3>理想情况（网络良好）：</h3>
                <div class="code-block">
                    <pre><code>docker build -t config-parser:latest .</code></pre>
                    <button class="copy-btn">复制</button>
                </div>

                <h3>现实问题（网络不佳）：</h3>
                <p>在中国大陆，直接执行上面的命令很可能会因为网络问题而失败。因为Docker默认会去国外的Docker Hub上拉取 <code>node:18-alpine</code> 这个基础镜像。</p>
                
                <div class="solution-box">
                    <h3>解决方案：使用本地镜像进行离线构建（这是精髓！）</h3>
                    <p><strong>第1步：获取基础镜像</strong></p>
                    <p>想办法在网络好的地方，或者通过朋友，先获取到 <code>node:18-alpine</code> 镜像，并导入到你本地。</p>
                    
                    <p><strong>第2步：查看本地镜像</strong></p>
                    <p>执行 <code>docker images</code> 命令，确保你本地已经有了这个镜像。你会看到类似下面的信息，记下它的 `IMAGE ID`。</p>
                    <div class="code-block">
                        <pre><code>REPOSITORY   TAG           IMAGE ID       CREATED         SIZE
node         18-alpine     f48cc5826852   11 months ago   127MB</code></pre>
                        <button class="copy-btn">复制</button>
                    </div>

                    <p><strong>第3步：给本地镜像打上"官方标签"</strong></p>
                    <p>这是最关键的一步！为了让 <code>Dockerfile</code> 在构建时能直接找到本地镜像而不去联网，我们需要给它一个"官方认证"的标签。使用上一步中获取到的镜像ID：</p>
                    <div class="code-block">
                        <pre><code>docker tag f48cc5826852 node:18-alpine</code></pre>
                        <button class="copy-btn">复制</button>
                    </div>

                    <p><strong>第4步：再次构建</strong></p>
                    <p>现在，再次运行构建命令，Docker会直接使用本地镜像，完美跳过网络拉取环节！</p>
                     <div class="code-block">
                        <pre><code>docker build -t config-parser:latest .</code></pre>
                        <button class="copy-btn">复制</button>
                    </div>
                </div>
            </section>
            
            <section id="section-5">
                <h2>6. 运行与访问：启动你的容器化应用</h2>
                <p>镜像构建好后，我们就可以用它来启动容器了。</p>
                <div class="code-block">
                    <pre><code>docker run -d --name config-parser-tool -p 3001:3001 config-parser:latest</code></pre>
                    <button class="copy-btn">复制</button>
                </div>
                <p>命令执行成功后，打开你的浏览器，访问 <code>http://localhost:3001</code>，就能看到我们的"配置字解析工具"页面了！</p>
                <a href="http://localhost:3001" target="_blank" class="button">立即访问</a>
            </section>

            <section id="section-6">
                <h2>7. 容器管理：常用的Docker命令</h2>
                <div class="grid-container">
                    <div class="grid-item">
                        <h4>查看运行容器</h4>
                        <div class="code-block">
                            <pre><code>docker ps</code></pre>
                            <button class="copy-btn">复制</button>
                        </div>
                    </div>
                    <div class="grid-item">
                        <h4>查看所有容器</h4>
                        <div class="code-block">
                            <pre><code>docker ps -a</code></pre>
                            <button class="copy-btn">复制</button>
                        </div>
                    </div>
                    <div class="grid-item">
                        <h4>停止容器</h4>
                        <div class="code-block">
                            <pre><code>docker stop &lt;容器名&gt;</code></pre>
                            <button class="copy-btn">复制</button>
                        </div>
                    </div>
                    <div class="grid-item">
                        <h4>启动容器</h4>
                        <div class="code-block">
                            <pre><code>docker start &lt;容器名&gt;</code></pre>
                            <button class="copy-btn">复制</button>
                        </div>
                    </div>
                     <div class="grid-item">
                        <h4>查看日志</h4>
                        <div class="code-block">
                            <pre><code>docker logs &lt;容器名&gt;</code></pre>
                            <button class="copy-btn">复制</button>
                        </div>
                    </div>
                    <div class="grid-item">
                        <h4>删除容器</h4>
                        <div class="code-block">
                            <pre><code>docker rm &lt;容器名&gt;</code></pre>
                            <button class="copy-btn">复制</button>
                        </div>
                    </div>
                </div>
            </section>

            <section id="section-7">
                <h2>8. 分享你的成果：镜像的导出与导入</h2>
                <p>现在，你已经有了一个包含完整应用的镜像，如何把它分享给别人呢？</p>
                <h3>第1步：导出（打包）镜像</h3>
                <p>在你电脑上执行（这会在当前目录下生成一个 <code>config-parser.tar</code> 文件）：</p>
                <div class="code-block">
                    <pre><code>docker save -o config-parser.tar config-parser:latest</code></pre>
                    <button class="copy-btn">复制</button>
                </div>
                <h3>第2步：导入（解包）镜像</h3>
                <p>你的朋友拿到 <code>config-parser.tar</code> 文件后，只需在他的电脑上执行：</p>
                <div class="code-block">
                    <pre><code>docker load -i config-parser.tar</code></pre>
                    <button class="copy-btn">复制</button>
                </div>
                <h3>第3步：运行导入的镜像</h3>
                <p>导入成功后，他的电脑上就有了和你完全一样的镜像。现在，他可以像你一样，直接通过 <code>docker run</code> 命令来启动应用容器了：</p>
                <div class="code-block">
                    <pre><code>docker run -d --name config-parser-tool -p 3001:3001 config-parser:latest</code></pre>
                    <button class="copy-btn">复制</button>
                </div>
                <p>然后他就可以在浏览器中访问 <code>http://localhost:3001</code> 了！这个过程完全不需要关心环境配置和网络问题。</p>
            </section>

            <section id="section-8">
                <h2>9. 总结与下一步</h2>
                <div class="highlight-box">
                    <h4>恭喜你！你已经成功地：</h4>
                    <ul>
                        <li><span class="emoji">✅</span> 理解了Docker的核心理念。</li>
                        <li><span class="emoji">✅</span> 学会了编写简单的 <code>Dockerfile</code>。</li>
                        <li><span class="emoji">✅</span> 掌握了离线构建镜像的关键技巧。</li>
                        <li><span class="emoji">✅</span> 学会了运行、管理和分享你的Docker应用。</li>
                    </ul>
                </div>
                <p>Docker的世界非常广阔，下一步你可以去探索 <code>docker-compose</code>，它可以用来管理多个相互关联的容器，让你的应用部署变得更加简单。</p>
                <p>希望这篇教程能帮你推开Docker的大门！</p>
            </section>

        </main>
    </div>

    <script src="tutorial_script.js"></script>
</body>
</html> 